package com.sweetcircle.aspect;


import com.sweetcircle.annotation.CheckObject;
import com.sweetcircle.annotation.CheckValue;
import com.sweetcircle.annotation.FieldNote;
import com.sweetcircle.annotation.GlobalInterceptor;
import com.sweetcircle.domain.dto.TokenUserInfoDto;
import com.sweetcircle.exception.BusinessException;
import com.sweetcircle.utils.AnanUtils;
import com.sweetcircle.utils.RedisComponet;
import jakarta.annotation.Resource;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component("globalOperatcionAspect")
public class GlobalOperatcionAspect {  // 全局切面  环绕通知  请求拦截

    @Resource
    RedisComponet redisComponet;

    @Pointcut("@annotation(com.sweetcircle.annotation.GlobalInterceptor)")  // 切面点  使用该注解的就会拦截
    private void requestInterceptor() {
    }

    // 以AccountController 类中的 login方法 login请求为例解释代码  进行 login请求

    @Before("requestInterceptor()")  // 前置通知
    public void interceptorDo(JoinPoint point) throws BusinessException, ClassNotFoundException, IllegalAccessException {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        Cookie[] cookies = request.getCookies();

        Object target = point.getTarget();  // 获取目标类对象  // AccountController类
        // 获取调用方法时传入的实参
        Object[] arguments = point.getArgs();  // 获取方法（切入点）的参数(传入的具体值)  // 切入点 执行的方法login方法  获取调用方法时传入的参数值 例 session（session 当前会话对象） root@qq.com（email 邮箱） C2DC426171FA602E28461ACB3E4FF204 （password 密码） 653424（checkCode 验证码
        MethodSignature signature = (MethodSignature) point.getSignature();  // 获取连接点的签名对象，再将其转换为MethodSignature类型
        Method method = signature.getMethod();  // 获取当前执行的方法对象
//            String methodName = method.getName();  // 获取方法的名称  // login
//            Class<?>[] parameterTypes = method.getParameterTypes();  // 获取方法形参的参数的类型  // HttpSession String String String
        // 通过反射获得拦截的method
//            Method method = target.getClass().getMethod(methodName, parameterTypes);
        // 获取该方法的注解 （判断是否添加有 GlobalInterceptor 注解）
        GlobalInterceptor interceptor = method.getAnnotation(GlobalInterceptor.class);
        if (interceptor != null) {
            /**
             * 校验登录
             */
            if (interceptor.checkLogin()) {

                TokenUserInfoDto tokenUserInfoDto = redisComponet.getTokenUserInfoDto(AnanUtils.getAuthorization(request));
                if (tokenUserInfoDto == null) {
                    throw new BusinessException(600, "用户未登录 或 登录超时 请登录！");
                }

            }
            /**
             * 校验参数
             */
            if (interceptor.checkParams()) {
                validateParams(method, arguments);
            }

            /**
             * 校验权限
             */
            if (interceptor.checkAdmin()) {
                // 获取当前登录用户信息
                // 校验 admin 管理员权限
            }

        }

    }

    /**
     * @param m         调用的方法对象
     * @param arguments 调用方法时传入的实参
     * @throws BusinessException
     */
    private void validateParams(Method m, Object[] arguments) throws ClassNotFoundException, IllegalAccessException {
        Parameter[] parameters = m.getParameters();  // 获取该方法对象的形参 所有形参的数组
        for (int i = 0; i < parameters.length; i++) {  // 遍历所有参数  校验每一个参数
            Parameter parameter = parameters[i];  // 获取该方法的形参对象
            // 否则添加有该注解 继续进行
            Object paramsObject = arguments[i];  // 获取调用方法时传入的实参的值  通过i 与形参一一对应 // root@qq.com
            CheckObject checkObject = parameter.getAnnotation(CheckObject.class);
            CheckValue checkValue = parameter.getAnnotation(CheckValue.class);  // 获取形参的指定注解 VerifyParam
            // 对象类型校验
            if (checkObject != null && checkObject.required()) {
                String typeName = parameter.getParameterizedType().getTypeName();  // // 获取该方法的形参对象的类型名称  com.easypan.package.className  完整的类名名称
                Class classz = Class.forName(typeName);
                Field[] fields = classz.getDeclaredFields();  // 获取声明的字段 属性
                String[] checkField = checkObject.checkProperties();  // 获取注解的checkProperties 的值 为数组 表示要校验的参数数组  默认是传入参数的属性名字符串组成的数
                if ("all".equals(checkField[0])) {  // 如果第一个参数值为 all 表示校验参数对象的所有属性
                    for (Field field : fields) {
                        // 设置该字段为可以访问标志
                        field.setAccessible(true);  // 将该字段设置为可访问
                        Object resultValue = field.get(paramsObject);
                        if (resultValue == null || "".equals(resultValue)) {
                            Map<String, String> fields2 = new HashMap<>();
                            for (Field field2 : fields) {
                                FieldNote fieldNote = field2.getAnnotation(FieldNote.class);
                                fields2.put(field2.getName(), fieldNote.value());
                            }
                            throw new BusinessException(400, "请求错误 缺少必要参数 " + field.getName() + " 参数参考 " + fields2);
                        }
                    }
                } else {  // 如果第一个参数值不为 all 表示校验参数对象的指定属性
                    for (Field field : fields) {
                        for (int j = 0; j < checkField.length; j++) {
                            if (field.getName().equals(checkField[j])) {  // 如果 该声明的字段 等于 需要校验的字段
                                field.setAccessible(true);  // 将该字段设置为可访问
                                Object resultValue = field.get(paramsObject);  // 在指定对象中获取指定字段的值
                                if (resultValue == null || "".equals(resultValue)) {
                                    Map<String, String> fields2 = new HashMap<>();
                                    for (Field field2 : fields) {
                                        for (int k = 0; k < checkField.length; k++){
                                            if (field2.getName().equals(checkField[k])){
                                                FieldNote fieldNote = field2.getAnnotation(FieldNote.class);
                                                fields2.put(field2.getName(), fieldNote.value());
                                            }
                                        }
                                    }
                                    throw new BusinessException(400, "请求错误 缺少必要参数 " + field.getName() + " 参数参考 " + fields2);
                                }
                            }
                        }
                    }
                }
            }
            // 基本类型校验
            if (checkValue != null && checkValue.required()) {
                if (AnanUtils.isEmpty(paramsObject)) {  // 校验传入的参数对象是否为空
                    Map<String, String> fields2 = new HashMap<>();
                    fields2.put(parameter.getName(), parameter.getAnnotation(FieldNote.class).value());
                    throw new BusinessException(400, "请求错误 缺少必要参数 " + parameter.getName() + " 参数参考 " + fields2);
                }
            }
        }
    }


}
